/* File: ast_decl.cc
 * -----------------
 * Implementation of Decl node classes.
 */
#include "ast_decl.h"
#include "ast_type.h"
#include "ast_stmt.h"
#include "errors.h" 
#include <typeinfo>

Decl::Decl(Identifier *n) : Node(*n->GetLocation()), scope(new Scope) {
   Assert(n != NULL);
   (id=n)->SetParent(this); 
}

void Decl::useScope(Scope *s) {
   scope->setParent(s);
}

VarDecl::VarDecl(Identifier *n, Type *t) : Decl(n) {
   Assert(n != NULL && t != NULL);
   (type=t)->SetParent(this);
}

void VarDecl::check() {
	if ( !type->checkPrim() ) {
		Scope *s = scope;
		while( s != NULL ) {
			Decl *decl;
			if ( (decl = s->map->Lookup(type->getName())) != NULL ) {
				if ( dynamic_cast<InterfaceDecl*>(decl) == NULL && dynamic_cast<ClassDecl*>(decl) == NULL ) {
					ReportError::IdentifierNotDeclared(decl->getId(),LookingForType);
				}
				return;
			}
			s = s->getParent();
		}
		if ( type->getId() != NULL ) {
			ReportError::IdentifierNotDeclared(type->getId(),LookingForType);
		}
	}
}

bool VarDecl::equals(Decl *d) {
   VarDecl *varDecl = dynamic_cast<VarDecl*>(d);
   if ( varDecl == NULL ) {
      return false;
   }
   return type->equals(varDecl->type);
}

ClassDecl::ClassDecl(Identifier *n, NamedType *ex, List<NamedType*> *imp, List<Decl*> *m) : Decl(n) {
   // extends can be NULL, impl & mem may be empty lists but cannot be NULL
   Assert(n != NULL && imp != NULL && m != NULL);     
   extends = ex;
   if (extends) extends->SetParent(this);
   (implements=imp)->SetParentAll(this);
   (members=m)->SetParentAll(this);
}

void ClassDecl::useScope(Scope *s) {
   scope->setClassDecl(this);
   scope->setParent(s);
   for (int i = 0; i < members->NumElements(); i++) {
      scope->insertDecl(members->Nth(i));
   }
   for (int i = 0; i < members->NumElements(); i++) {
      members->Nth(i)->useScope(scope);
   }
}

void ClassDecl::check() {
	// Check each member
	for ( int i = 0; i < members->NumElements(); i++) {
		members->Nth(i)->check();
	}        
	// Check that extension classes exist
	checkExtendsDefined();	
	// Check that implemtations exist
	checkImplementsDefined();
	// Check that all function defs are OK
	for (int i = 0; i < implements->NumElements(); i ++ ) {
		checkMembersImplemented(implements->Nth(i));
	}
   
	checkMembersExtended(extends);
  checkInterfacesImplemented();
   
}
void ClassDecl::checkInterfacesImplemented() {
   Scope *s = scope->getParent();
   for(int i = 0; i < implements->NumElements(); i++) {
      NamedType *n = implements->Nth(i);
      Decl *l = s->map->Lookup(implements->Nth(i)->getName());
      InterfaceDecl *i = dynamic_cast<InterfaceDecl*>(l);
      if ( i == NULL ) {
         continue;
      }
      List<Decl*> *m = i->getMembers();
      for(int i = 0; i < m->NumElements(); i++) {
         Decl *decl = m->Nth(i);
         ClassDecl *c = this;
         Decl *classFind;
         while ( classFind != NULL ) {
            classFind = c->getScope()->map->Lookup(decl->getName());
            if ( classFind != NULL ){
               break;
            }
            if ( c->getExtends() == NULL ) {
               c = NULL;
            } else {
               const char *extendsName = c->getExtends()->getName();
               Decl *e = Program::gScope->map->Lookup(extendsName);
               c = dynamic_cast<ClassDecl*>(e);
            }
            
         }
         if ( classFind == NULL ) {
            ReportError::InterfaceNotImplemented(this,n);
            return;
         }
      }
   }
}


void ClassDecl::checkMembersImplemented(NamedType *type) {
	Decl *find = scope->getParent()->map->Lookup(type->getName());
	InterfaceDecl *i = dynamic_cast<InterfaceDecl*>(find);
	if ( i != NULL ) {
		checkScope(i->getScope());
	}
}

void ClassDecl::checkMembersExtended(NamedType *type) {
	if ( type != NULL ) {
		Decl *find = scope->getParent()->map->Lookup(type->getName());
		ClassDecl *decl = dynamic_cast<ClassDecl*>(find);
		if ( decl != NULL ) {
			checkMembersExtended(decl->extends);
			checkScope(decl->scope);
		}
	}
}

void ClassDecl::checkScope(Scope *s) {
	Iterator<Decl*> iter = scope->map->GetIterator();
	Decl *decl;
	while((decl = iter.GetNextValue()) != NULL) {
		Decl *find = s->map->Lookup(decl->getName());
		if ( find == NULL ) {
			continue;
		}
		if ( dynamic_cast<VarDecl*>(find) != NULL ) {
			ReportError::DeclConflict(decl,find);
		}
		if ( dynamic_cast<FnDecl*>(find) != NULL ) {
         if ( !decl->equals(find) ) {
            ReportError::OverrideMismatch(decl);
         } 
		}
	}
}

void ClassDecl::checkExtendsDefined() {
	if (extends != NULL) {
		Decl *find = scope->getParent()->map->Lookup(extends->getName());
		if ( dynamic_cast<ClassDecl*>(find) == NULL ) {
			ReportError::IdentifierNotDeclared(extends->getId(),LookingForClass);
		}
	}
}

void ClassDecl::checkImplementsDefined() {
	Scope *s = scope->getParent();
	for(int i = 0; i < implements->NumElements(); i++) {
		NamedType *n = implements->Nth(i);
		Decl *find = scope->getParent()->map->Lookup(n->getName());
		if ( dynamic_cast<InterfaceDecl*>(find) == NULL ) {
			ReportError::IdentifierNotDeclared(n->getId(),LookingForInterface);
		}
	}
}

InterfaceDecl::InterfaceDecl(Identifier *n, List<Decl*> *m) : Decl(n) {
   Assert(n != NULL && m != NULL);
   (members=m)->SetParentAll(this);
}

void InterfaceDecl::check() {
	for(int i = 0;i < members->NumElements(); i++){
		members->Nth(i)->check();
	}        
}

void InterfaceDecl::useScope(Scope *s) {
   scope->setParent(s);
   for (int i = 0; i < members->NumElements(); i++ ) {
      scope->insertDecl(members->Nth(i));
   }
   for (int i = 0; i < members->NumElements(); i++ ) {
      members->Nth(i)->useScope(scope);
   }
}

FnDecl::FnDecl(Identifier *n, Type *r, List<VarDecl*> *d) : Decl(n) {
   Assert(n != NULL && r!= NULL && d != NULL);
   (returnType=r)->SetParent(this);
   (formals=d)->SetParentAll(this);
   body = NULL;
}

void FnDecl::check() {
   for(int i = 0; i < formals->NumElements(); i++) {
		formals->Nth(i)->check();
	}
	if ( body != NULL ) {
		body->check();
	}
}

void FnDecl::useScope(Scope *s) {
   scope->setParent(s);
   scope->setFnDecl(this);
   for (int i = 0; i < formals->NumElements(); i++) {
      scope->insertDecl(formals->Nth(i));
   }
   for (int i = 0; i < formals->NumElements(); i++) {
      formals->Nth(i)->useScope(scope);
   }
   if ( body != NULL ) {
      body->useScope(scope);
   }
}

void FnDecl::SetFunctionBody(Stmt *b) { 
   (body=b)->SetParent(this);
}

bool FnDecl::equals(Decl *d) {
	FnDecl *f = dynamic_cast<FnDecl*>(d);
	if ( f == NULL ) {
		return false;
	}
	if ( returnType != f->returnType ) {
		return false;
	}
	if ( formals->NumElements() != f->formals->NumElements()) {
		return false;
	}
	for ( int i = 0; i < formals->NumElements(); i++ ) {
      if ( !formals->Nth(i)->equals(f->formals->Nth(i)) ) {
         return false;
      }
   }
	return true;
   
}

